package Group9.TodoManager.View;

import Group9.TodoManager.Actions.UpdateTaskAction;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import javax.swing.BorderFactory;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.table.TableModel;
import Group9.TodoManager.Controller.ITaskController;
import Group9.TodoManager.Data.Interface.ITableModel;
import Group9.TodoManager.Data.Task;
import Group9.TodoManager.DataStore.TaskMarshaller;
import Group9.TodoManager.DataStore.xmlDataRepository;
import Group9.TodoManager.Settings;
import Group9.TodoManager.SimpleCellEditor;
import Group9.TodoManager.SliderTableCell;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeSelectionModel;
import org.jdom.JDOMException;

/**
 * This class is used to create the application main GUI
 * @author Rupesh
 * @author volkan cambazoglu
 */
public class TodoManagerGUI<T> extends JFrame {

    private JTable table;
    private JPopupMenu menus;
    private JTextArea txtarea;
    private JSplitPane mainSplitPane;
    private JSplitPane contentSplitPane;
    private JTree filterTree;
    private JPanel navigationPanel;
    private JList upcomingList;
    private JMenuBar menuBar;
    private JToolBar toolBar;
    private JLabel upcomingTasksLabel;
    private JProgressBar progressBar;
    private JLabel progressLabel;
    private JPanel bottomPanel;
    private JPanel bottomInnerPanel;
    private TodoManagerLookAndFeel theme;
    private ITaskController<T> controller;
    private ListUI listUI;

    /**
     * Constructor that takes controller as parameter. Creates an instance
     * of main GUI
     * @param control controller
     */
    public TodoManagerGUI(ITaskController<T> control) {
        this("Java Application Test", control);
    }

    /**
     * Constructor that takes title and controller as parameter. Creates an 
     * instance of main GUI
     * @param title title of the application
     * @param control controller
     */
    public TodoManagerGUI(String title, ITaskController<T> control) {
        theme = new TodoManagerLookAndFeel(Settings.getTheme());
        JFrame.setDefaultLookAndFeelDecorated(true);
        progressBar = new JProgressBar();
        bottomPanel = new JPanel(new BorderLayout());
        bottomInnerPanel = new JPanel(new BorderLayout());
        progressLabel = new JLabel(Settings.getCurrentResourceBundle().getString("ProgressOfTask"));
        setTitle(title);
        controller = control;
        table = new JTable((TableModel) controller.getTableModel());
        menus = getTableRowContextMenu();
        txtarea = new JTextArea();
        mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        contentSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        contentSplitPane.setOneTouchExpandable(true);
        filterTree = getFilterTree();
        navigationPanel = new JPanel(new BorderLayout());
        upcomingList = new JList();
        menuBar = getApplicationMenuBar();
        toolBar = getToolBar();
        upcomingTasksLabel = new JLabel(
           Settings.getCurrentResourceBundle().getString("Title.Today"));

        //NOW ASSIGN DIFFERENT MODELS FOR THE APPROPRIATE UI COMPONENT

        // Pop-up list
        listUI = new ListUI();

        //INITIALIZE THE GUI
        initialize();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setIconImage(new ImageIcon(getClass().getResource(
                "/Group9/TodoManager/Resource/TodoManager.png")).getImage());

        this.addWindowListener(new WindowAdapter() {
        	
            @Override
            public void windowClosing(WindowEvent e) {
                controller.getExitAction().executeAction(e);
            }
        });
    }

    /**
     * This is called to initialize the main GUI.
     */
    private void initialize() {
        mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        mainSplitPane.setAutoscrolls(true);
        mainSplitPane.setDividerLocation(200);

        //SET THE CONTENTS IN THE GUI
        //ADD MENUBAR
        this.setJMenuBar(menuBar);
        //ADD TOOLBAR
        JPanel pan = new JPanel(new BorderLayout());
        CustomDateTimeDisplay timeDisplay = new CustomDateTimeDisplay();
        timeDisplay.Start();
        pan.add(toolBar, BorderLayout.CENTER);
        pan.add(timeDisplay, BorderLayout.EAST);

        this.getContentPane().add(pan, BorderLayout.NORTH);
        //ADD SPLIT PANE
        this.getContentPane().add(mainSplitPane, BorderLayout.CENTER);
        //ADD STATUSBAR
        this.getContentPane().add(new StatusBar(), BorderLayout.SOUTH);

        //NOW ADD THE CONTENTS OF THE MAIN SPLIT PANE
        mainSplitPane.setLeftComponent(new JScrollPane(getNavigationPanel()));
        mainSplitPane.setRightComponent(contentSplitPane);

        //NOW ADD THE CONTENTS OF THE CONTENT SPLIT PANE
        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setBorder(BorderFactory.createTitledBorder(
                Settings.getCurrentResourceBundle().getString("TableTitle")));

        contentSplitPane.setLeftComponent(scrollPane);
        bottomInnerPanel.add(progressLabel, BorderLayout.PAGE_START);
        bottomInnerPanel.add(progressBar, BorderLayout.CENTER);
        bottomPanel.add(bottomInnerPanel, BorderLayout.PAGE_START);
        bottomPanel.add(new JScrollPane(txtarea), BorderLayout.CENTER);
        contentSplitPane.setRightComponent(bottomPanel);
        contentSplitPane.setDividerLocation(400);

        //ADD CONTEXT MENU TO EACH ROW IN TABLE
        table.setComponentPopupMenu(getTableRowContextMenu());
        table.getTableHeader().setReorderingAllowed(false);
        table.setAutoCreateRowSorter(true);
        table.getSelectionModel().setSelectionMode(
                ListSelectionModel.SINGLE_SELECTION);

        table.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {

                    public void valueChanged(ListSelectionEvent e) {
                        controller.getTableModel().setSelectedRowIndex(
                                Integer.valueOf(
                                table.getSelectionModel().
                                getMinSelectionIndex()));
                    }
                });

        table.setRowSelectionAllowed(true);

        controller.getTableModel().addPropertyChangeListener(
                new PropertyChangeListener() {

                    public void propertyChange(PropertyChangeEvent evt) {
                        if (evt.getPropertyName().equalsIgnoreCase(
                                "SelectedRowIndex")) {
                        	
                            int rowindex = Integer.parseInt(
                                    evt.getNewValue().toString());
                            if (rowindex >= 0) {
                                table.setRowSelectionInterval(
                                        rowindex, rowindex);
                            }
                        }
                    }
                });

        SimpleCellEditor editor = new SimpleCellEditor();
        editor.addCellEditorListener(new CellEditorListener() {

            public void editingStopped(ChangeEvent e) {
                new UpdateTaskAction<T>(
                        (ITableModel<T>) Settings.getTableModel(),
                        Settings.getRepository()).executeAction(
                        (((JSlider) e.getSource()).getValue()));
            }

            public void editingCanceled(ChangeEvent e) {
            }
        });

        table.setDefaultEditor(JSlider.class, editor);
        table.setDefaultRenderer(JSlider.class, new SliderTableCell());

        filterTree.addTreeSelectionListener(new TreeSelectionListener() {

            public void valueChanged(TreeSelectionEvent e) {
                controller.getLoadAction().executeAction(e);
            }
        });
    }

    /**
     * Getter method of text area that is placed in the south panel
     * @return txtarea
     */
    public JTextArea getTxtarea() {
        return txtarea;
    }
    
    /**
     * Getter method for pop up list
     * @return listUI
     */
    public ListUI getListUI() {
		return listUI;
	}

    /**
     * Removes the provided item from the list
     * @param item will be removed
     */
    public void removeFromList(Object item) {
		listUI.removeFromList(item);
	}
    
    /**
     * Updates the provided item in the list
     * @param index index of the item in the list
     * @param item item to be updated
     */
    public void editListElement(int index, Object item) {
		listUI.updateList(item, index);
	}
    
    /**
     * Adds the provided item to the list
     * @param item will be added to the list
     */
    public void addToList(Object item) {
		listUI.addToList(item);
	}

	/**
     * Reset text area
     */
    public void resetTxtarea() {
		txtarea.setText(Settings.getCurrentResourceBundle().getString("TextArea"));
	}

	/**
     * Used to set the model of the progress bar.
     * @param max max value of the progress bar
     * @param now actual value of the progress bar
     */
    public void setProgressModel(int max, int now) {
        DefaultBoundedRangeModel model = new DefaultBoundedRangeModel();
        model.setRangeProperties(now, 1, 0, max, false);
        progressBar.setModel(model);
    }

    /**
     * Resets the progress model
     * It is used when no task is selected or existing
     */
    public void resetProgressModel() {
        DefaultBoundedRangeModel model = new DefaultBoundedRangeModel();
        model.setRangeProperties(0, 1, 0, 5, false);
        progressBar.setModel(model);
    }

    /**
     * Updates the upcomingList
     * UpcomingList is the list that is located at the south west panel of
     * the mainGUI.
     */
    public void setUpcomingList() {
    	upcomingList.setModel(getListModel());
	}

	/**
     * After the GUI is initialized it is loaded with default values
     */
    public void loadControls() {
        filterTree.setSelectionInterval(0, 0);
        listUI.initializeList();
        upcomingList.setModel(getListModel());
    }

    /**
     * This is a dummy list representing the upcoming tasks.
     * @return
     */
    private DefaultListModel getListModel() {
        DefaultListModel model = new DefaultListModel();

        for(int i=0; i<listUI.getList().getModel().getSize(); i++){
        	model.addElement(((Task)listUI.getList().getModel().getElementAt(i)).getTitle());
        }
        return model;
    }

    /**
     * This class returns the navigational panel content pane
     * @return
     */
    private JPanel getNavigationPanel() {
        //FILTERING TREE MUST BE ADDED AT TOP IN THE NAVIGATION PANEL
        navigationPanel.add(new JScrollPane(filterTree), BorderLayout.NORTH);

        //ADD UPCOMING LIST LABEL TO SHOW A HEADER FOR THE UPCOMING TASKS.
        //ADD UPCOMING TASKS TO THE LIST AND SHOW IT IN BOTTOM OF NAVIGATION PANEL
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(upcomingTasksLabel, BorderLayout.NORTH);
        panel.add(upcomingList, BorderLayout.CENTER);
        JButton detachList = new JButton("Detach List");
        detachList.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				listUI.setVisible(true);
			}
		});
        panel.add(detachList, BorderLayout.SOUTH);
        navigationPanel.add(panel, BorderLayout.CENTER);

        return navigationPanel;
    }

    /**
     * This returns the Menubar of the application
     * @return
     */
    private JMenuBar getApplicationMenuBar() {
        //CREATE MENU BAR
        JMenuBar menubar = new JMenuBar();
        String s = Settings.getCurrentResourceBundle().getString("TableTitle");

        JMenu file = new JMenu(
                Settings.getCurrentResourceBundle().getString("FileMenu"));
        file.add(new JMenuItem(controller.getCreateAction()));
        file.add(new JMenuItem(controller.getEditAction()));
        file.add(new JMenuItem(controller.getRemoveAction()));
        file.add(new JMenuItem(controller.getCompleteAction()));
        file.add(new JMenuItem(controller.getIncompleteAction()));
        file.add(new JMenuItem(controller.getExitAction()));


        //THE LANGUAGE MENU IS HARD CODED... THIS NEEDS TO BE CHANGED AND
        //WILL BE CHANGED LATER
        JMenu edit = new JMenu(
                Settings.getCurrentResourceBundle().getString("EditMenu"));
        JMenu language = new JMenu(
                Settings.getDefaultResourceBundle().getString("LanguageMenu"));
        edit.add(language);
        
        ButtonGroup group = new ButtonGroup();

        String lang =
                Settings.getPreference().getProperty(
                "Language", "en").toString();

        JRadioButtonMenuItem english = new JRadioButtonMenuItem(
                Settings.getDefaultResourceBundle().getString("EnglishMenu"),
                lang.equals("en"));
        JRadioButtonMenuItem swedish = new JRadioButtonMenuItem(
                Settings.getDefaultResourceBundle().getString("SwedishMenu"),
                lang.equals("sv"));
        group.add(english);
        group.add(swedish);
        language.add(english);
        language.add(swedish);
        english.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Settings.getPreference().put("Language", "en");
                Settings.getPreference().put("Country", "us");
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.LanguageChanged"));
            }
        });

        swedish.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Settings.getPreference().put("Language", "sv");
                Settings.getPreference().put("Country", "SE");
                Settings.setLocale("sv", "SE");
                Settings.getMainGUI().repaint();
                Settings.getMainGUI().validate();
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.LanguageChanged"));
            }
        });
        
        ButtonGroup temaGroup = new ButtonGroup();
        JMenu view = new JMenu(
        		Settings.getCurrentResourceBundle().getString("ViewMenu"));
        
        String tema =
            Settings.getPreference().getProperty("Theme", "MetalTheme2").toString();
        
        JRadioButtonMenuItem defaultTheme = new JRadioButtonMenuItem(
        		Settings.getCurrentResourceBundle().getString("DefaultTheme"), 
        		tema.equalsIgnoreCase("DefaultTheme"));
        JRadioButtonMenuItem metalTheme1 = new JRadioButtonMenuItem(
        		Settings.getCurrentResourceBundle().getString("MetalTheme1"), 
        		tema.equalsIgnoreCase("MetalTheme1"));
        JRadioButtonMenuItem metalTheme2 = new JRadioButtonMenuItem(
        		Settings.getCurrentResourceBundle().getString("MetalTheme2"), 
        		tema.equalsIgnoreCase("MetalTheme2"));
        JRadioButtonMenuItem systemTheme = new JRadioButtonMenuItem(
        		Settings.getCurrentResourceBundle().getString("SystemTheme"), 
        		tema.equalsIgnoreCase("SystemTheme"));
        
        defaultTheme.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				Settings.getPreference().put("Theme", "DefaultTheme");
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.ThemeChanged"));
			}
		});
        metalTheme1.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				Settings.getPreference().put("Theme", "MetalTheme1");
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.ThemeChanged"));
			}
		});
        metalTheme2.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				Settings.getPreference().put("Theme", "MetalTheme2");
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.ThemeChanged"));
			}
		});
        systemTheme.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				Settings.getPreference().put("Theme", "SystemTheme");
                Settings.displayMessage(
                	Settings.getCurrentResourceBundle().getString(
                		"Message.ThemeChanged"));
			}
		});
        temaGroup.add(defaultTheme);
        temaGroup.add(metalTheme1);
        temaGroup.add(metalTheme2);
        temaGroup.add(systemTheme);
        view.add(defaultTheme);
        view.add(metalTheme1);
        view.add(metalTheme2);
        view.add(systemTheme);

        JMenu help = new JMenu(
                Settings.getCurrentResourceBundle().getString("HelpMenu"));
        help.add(new JMenuItem(controller.getAboutAction()));
        menubar.add(file);
        menubar.add(edit);
        menubar.add(view);
        menubar.add(help);

        return menubar;
    }

    /**
     * This returns the toolbar of the main application GUI
     * @return
     */
    private JToolBar getToolBar() {
        //CREATE TOOLBAR
        JToolBar toolbar = new JToolBar();
        //DISABLING THE MOVABILITY OF THE TOOLBAR SO THAT IT CANNOT BE DRAGGED
        toolbar.setFloatable(false);

        toolbar.add(controller.getCreateAction());
        toolbar.add(controller.getEditAction());
        toolbar.add(controller.getRemoveAction());
        toolbar.add(controller.getCompleteAction());
        toolbar.add(controller.getIncompleteAction());
        toolbar.add(controller.getExitAction());

        return toolbar;
    }

    /**
     * This returns the context menu instance for the JTable
     * @return
     */
    private JPopupMenu getTableRowContextMenu() {
        JPopupMenu menu = new JPopupMenu();

        menu.add(new JMenuItem(controller.getCreateAction()));
        menu.add(new JMenuItem(controller.getEditAction()));
        menu.add(new JMenuItem(controller.getRemoveAction()));
        menu.add(new JMenuItem(controller.getCompleteAction()));
        menu.add(new JMenuItem(controller.getIncompleteAction()));

        return menu;
    }

    /**
     * Returs the Tree control to show in the navigational panel
     * @return Tree control to show in navigational panel.
     */
    private JTree getFilterTree() {
        TreeModel model = controller.getTreeModel();
        JTree tree = new JTree(controller.getTreeModel());
        tree.setRootVisible(false);

        tree.getSelectionModel().setSelectionMode(
                TreeSelectionModel.SINGLE_TREE_SELECTION);

        //TO EXPAND ALL THE NODES OF THE TREE
        for(int i = 0; i < tree.getRowCount(); i++)
            tree.expandRow(i);

        return tree;
    }

    /**
     * Main method of the application. When the application is executed,
     * execution starts from here. The user interface of the program is created
     * and made visible.
     * @param args
     */
    public static void main(String[] args) {

        /**
         * Initialize the repository
         */
        try {
            Settings.setRepository(
                    new xmlDataRepository<Task>(
             System.getProperty("user.home")+"/Group9_ToDoManager/TodoManagerXml.xml",
                    new TaskMarshaller()));

        } catch (JDOMException ex) {
            Logger.getLogger(
                    Settings.getMainGUI().getClass().getName()).
                    log(Level.SEVERE,
                    null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Settings.getMainGUI().getClass().getName()).
                    log(Level.SEVERE,
                    null, ex);
        }

        /**
         * This method helps to update the GUI while some other thread is
         * running. The GUI is not blocked while another thread is working.
         */
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                Properties pref = Settings.getPreference();

                Settings.setLocale(pref.getProperty("Language", "en").toString(),
                        pref.getProperty("Country", "US").toString());
                
                Settings.setTheme(pref.getProperty("Theme", "MetalTheme2"));

                TodoManagerGUI todoManager = Settings.getMainGUI();

                todoManager.loadControls();

                System.out.println(System.getProperty("user.home"));

                String x = "340", y = "100", width = "600", height = "600";

                todoManager.setLocation(
                        (int) Double.parseDouble(
                        pref.getProperty("WindowPositionX", x)),
                        (int) Double.parseDouble(
                        pref.getProperty("WindowPositionY", y)));

                todoManager.setSize(
                        Integer.parseInt(
                        pref.getProperty("WindowWidth", width)),
                        Integer.parseInt(
                        pref.getProperty("WindowHeight", height)));

               // todoManager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                todoManager.setVisible(true);
            }
        });
    }
}